
/****************************************************************************/

static void setDraggingComponent(kernelWindowComponent *component)
{
	if (draggingComponent == component)
		return;

	if (draggingComponent)
	{
		// TODO: Send EVENT_MOUSE_DRAG_END
	}

	draggingComponent = component;
}

/****************************************************************************/

static void mouseEnterWindow(kernelWindow *window)
{
	if (window == mouseInWindow)
		return;

	windowEvent event;
	kernelMemClear(&event, sizeof(windowEvent));

	if (mouseInWindow)
	{
		event.type = EVENT_MOUSE_EXIT;
		kernelWindowEventStreamWrite(&(mouseInWindow->events), &event);
	}

	mouseInWindow = window;

	if (window)
	{
		event.type = EVENT_MOUSE_ENTER;
		kernelWindowEventStreamWrite(&(window->events), &event);
		if (window->pointer > 0)
			kernelMouseSetPointer(window->pointer);
	}
}

/****************************************************************************/

static int processEvents(void)
{
	// This loops through the general mouse/keyboard event streams, and
	// generally directs events to the appropriate window and/or window
	// components.

	windowEvent event;
	windowEvent tmpEvent;
	kernelWindow *window = NULL;
	kernelWindowComponent *targetComponent = NULL;
	kernelWindowComponent *contextMenu = NULL;

	int EventCount = 0;

	kernelMouseDrawReal();

	ACQUIRE_WINDOW_LIST
		return (EventCount);

	while (kernelWindowEventStreamRead(&rawEvents, &event) > 0)
	{
		// We have a mouse event.

		EventCount++;

		int dragging_begin = 0;


		if (event.type & EVENT_MASK_KEY)
		{
			// Find the target component
			if (!focusWindow)
				continue;

			// If it was a [tab] down, focus the next component
			if (event.type == EVENT_KEY_DOWN && event.key == 9)
			{
				if (focusWindow->focusComponent == NULL ||
				    !(focusWindow->focusComponent->params.flags & WINDOW_COMPFLAG_STICKYFOCUS) )
				{
					kernelWindowFocusNextComponent(focusWindow);
					continue;
				}
			}

			if (!focusWindow->focusComponent)
				continue;

			targetComponent = focusWindow->focusComponent;

			if (targetComponent->keyEvent)
				targetComponent->keyEvent(targetComponent, &event);
	  
			// Put this key event into the component's windowEventStream
			kernelWindowEventStreamWrite(&(targetComponent->events), &event);

			continue;
		}

		kernelMouseDrawReal();

		if (event.type == EVENT_MOUSE_MOVE)
		{
			setDraggingComponent(NULL);

			// If there's another move event pending, skip to it, since we don't
			// care about where the mouse *used* to be.  We only care about the
			// current state.
			if (kernelWindowEventStreamPeek(&rawEvents) == EVENT_MOUSE_MOVE)
				continue;

			// Figure out in which window the mouse moved, if any

			window = getEventWindow(event.xPosition, event.yPosition);

			mouseEnterWindow(window);

			continue;
		}

		// If we are dragging a component, we know the target window
		// and component already
		if (draggingComponent)
		{
			// If there's another dragging event pending, skip to it
			if (kernelWindowEventStreamPeek(&rawEvents) == EVENT_MOUSE_DRAG)
				continue;

			window = draggingComponent->window;
			targetComponent = draggingComponent;
		}

		// If we have an active menu, and the mouse event is inside
		// the menu, skip looping through the window list
		else if (activeMenu &&
		         isPointInside(event.xPosition, event.yPosition, makeComponentScreenArea(activeMenu)))
		{
			window = activeMenu->window;
			targetComponent = activeMenu;
			kernelDebug(debug_gui, "Mouse event in active menu (window %s)", window->title);
		}

		else
		{
			// Figure out which window this is happening to, if any

			if (mouseInWindow)
				window = mouseInWindow;
			else
				window = getEventWindow(event.xPosition, event.yPosition);

			if (window == NULL)
				continue;

			// The event was inside a window

			kernelDebug(debug_gui, "Mouse event in window %s", window->title);

			// Find out if it was inside of any of the window's components.
			targetComponent = getEventComponent(window, event.xPosition, event.yPosition);

			// If it was a click...
			if (event.type & EVENT_MOUSE_DOWN)
			{
			
				// If the window has a dialog window, focus the dialog instead and we're finished.
				if (window->dialogWindow)
				{
					if (window->dialogWindow != focusWindow)
						changeWindowFocus(window->dialogWindow);

					RELEASE_WINDOW_LIST
					return EventCount;
				}
				
				// If the window is not in focus, give it the focus.
				if (window != focusWindow)
					changeWindowFocus(window);

				// Focus the new component.
				if (window->focusComponent != targetComponent)
				{
					if (targetComponent && (targetComponent->flags & WINFLAG_CANFOCUS))
					{
						kernelWindowChangeComponentFocus(window, targetComponent);
					}
					else if (activeMenu)
					{
						// The active menu simply lost the focus (as in, a click in
						// empty space.  Try to give the focus back to whatever had
						// it previously, or else just focus the first thing.  The
						// main idea is that the active menu has to lose it.
						if (window->oldFocusComponent)
							kernelWindowChangeComponentFocus(window, window->oldFocusComponent);
						else
							focusFirstComponent(window);
					}
				}
			}
		}

		if (targetComponent)
		{
			if (targetComponent->mouseEvent)
				targetComponent->mouseEvent(targetComponent, &event);

			kernelMemCopy(&event, &tmpEvent, sizeof(event));
	  
			// Adjust to the coordinates of the component
			tmpEvent.xPosition -= (window->xCoord + targetComponent->xCoord);
			tmpEvent.yPosition -= (window->yCoord + targetComponent->yCoord);

			// Put this mouse event into the component's windowEventStream
			kernelWindowEventStreamWrite(&(targetComponent->events), &tmpEvent);

			if (event.type == EVENT_MOUSE_DRAG || event.type & EVENT_MOUSE_DOWN)
			{
				setDraggingComponent(targetComponent);
				dragging_begin = 1;
			}
		}

		if (event.type == EVENT_MOUSE_RIGHTDOWN)
		{
			if (targetComponent)
				contextMenu = targetComponent->contextMenu;

			if (!contextMenu)
				contextMenu = window->contextMenu;

			if (contextMenu)
			{
				kernelDebug(debug_gui, "Show context menu");

				// Adjust to the coordinates of the window
				contextMenu->xCoord = (event.xPosition - window->xCoord);
				contextMenu->yCoord = (event.yPosition - window->yCoord);

				// Shouldn't go off the screen
				if ((event.xPosition + contextMenu->width) > screenWidth)
					contextMenu->xCoord -= ((event.xPosition + contextMenu->width) - screenWidth);
				if ((event.yPosition + contextMenu->height) > screenHeight)
					contextMenu->yCoord -= ((event.yPosition + contextMenu->height) - screenHeight);

				kernelWindowComponentSetVisible(contextMenu, 1);
			}
		}

		// If we were dragging something, have we stopped dragging it?
		if (!dragging_begin && draggingComponent && (event.type != EVENT_MOUSE_DRAG))
			setDraggingComponent(NULL);
	}

	RELEASE_WINDOW_LIST

	return EventCount;
}

/****************************************************************************/

static int processSysContainerEvents(kernelWindow* window)
{
	int EventCount = 0;

	if (!window)
		return EventCount;
	if (!window->sysContainer)
		return EventCount;

	int compCount;
	int processId = window->processId;
	kernelWindowContainer* container = window->sysContainer->data;

	if (!container)
		return EventCount;

	// Loop through the system components
	for (compCount = 0; compCount < container->numComponents; compCount ++)
	{
		windowEvent event;
		kernelWindowComponent* component = container->components[compCount];

		if (!component)
			continue;

		if (!component->eventHandler)
			continue;

		// Any handler for the event?  Any events pending?
		if (kernelWindowEventStreamRead(&(component->events), &event) > 0)
		{
			EventCount++;
			component->eventHandler(component, &event);

			// Window closed?  Don't want to loop here any more.
			if (!kernelMultitaskerProcessIsAlive(processId))
				break;
		}
	}

	return EventCount;
}

/****************************************************************************/

__attribute__((noreturn))
static void rawEventDispatcherThread(void)
{
	// This thread runs as the 'window thread' to watch for window events
	// on 'system' GUI components such as window close buttons.

	while(1)
	{
		kernelMultitaskerYield();
		processEvents();
	}
}


/****************************************************************************/

__attribute__((noreturn))
static void windowMaintenanceThread(void)
{
	// This thread runs as the 'window thread' to watch for window events
	// on 'system' GUI components such as window close buttons.

	kernelWindow *window = NULL;
	int processId = 0;
	int winCount;

	int EventCount = 0;
	int i = 0;

	while(1)
	{
		if (EventCount == 0)
			i++;
		else
			i = 0;

		if (i < 10)
			kernelMultitaskerYield();
		else
			kernelMultitaskerWaitMcs(70000);

		EventCount = 0;

		// Loop through all of the windows, looking for events in 'system'
		// components

		// Lock the window list
		if (kernelLockGet(&windowListLock) < 0)
			continue;

		for (winCount = 0; winCount < numberWindows; winCount ++)
		{
			window = windowList[winCount];

			if (!window)
			{
				kernelError(kernel_warn, "NULL value in windowList!");
				break;
			}

			processId = window->processId;

			// Check to see whether the process that owns the window is still
			// alive.  If not, destroy the window and quit for this event.
			if (!kernelMultitaskerProcessIsAlive(processId))
			{
				kernelWindowDestroy(window);
				break;
			}

			EventCount += processSysContainerEvents(window);
		}

		kernelLockRelease(&windowListLock);

		// Done
	}
}

/****************************************************************************/

static void windowThreadsRespawn(void)
{
	if (kernelMultitaskerProcessIsAlive(rawEventDispatcherThreadPid)
	&&  kernelMultitaskerProcessIsAlive(windowMaintenanceThreadPid))
		return;

	ACQUIRE_WINDOW_LIST
		return;

	if (!kernelMultitaskerProcessIsAlive(rawEventDispatcherThreadPid))
	{
		rawEventDispatcherThreadPid = kernelMultitaskerSpawnKernelThread(
			rawEventDispatcherThread, "event dispatcher", 0, NULL);
	}

	if (!kernelMultitaskerProcessIsAlive(windowMaintenanceThreadPid))
	{
		windowMaintenanceThreadPid = kernelMultitaskerSpawnKernelThread(
			windowMaintenanceThread, "window maintenance", 0, NULL);
	}

	RELEASE_WINDOW_LIST
}

/****************************************************************************/

